package com.yuheadline.user.service.impl;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dust.yuheadline.common.aliyun.oss.OssFileUploader;
import com.dust.yuheadline.common.aliyun.utils.AliOssProperties;
import com.dust.yuheadline.common.exception.CustomException;
import com.dust.yuheadline.common.utils.ResponseCode;
import com.dust.yuheadline.common.utils.Result;
import com.yuheadline.model.user.domain.User;
import com.yuheadline.model.user.dto.UserLoginDto;
import com.yuheadline.model.user.dto.UserRegisterDto;
import com.yuheadline.model.user.enums.Status;
import com.yuheadline.model.user.vo.GetUserInfoVo;
import com.yuheadline.model.user.vo.UserLoginVo;
import com.yuheadline.model.user.vo.UserRegisterVo;
import com.yuheadline.user.mapper.UserMapper;
import com.yuheadline.user.service.UserService;
import com.yuheadline.user.util.ThreadLocalUtil;
import com.yuheadline.utils.encryption.SHAUtil;
import com.yuheadline.utils.jwt.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.springframework.beans.BeanUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;

/**
 * @author 高翔宇
 * @since 2024/4/30 周二 上午10:27
 */
@Service
@RequiredArgsConstructor
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private final UserMapper userMapper;
    private final OssFileUploader ossFileUploader;
    private final AliOssProperties aliOssProperties;
    private final OSS oss;

    /**
     * 登录
     */
    @Override
    public Result<UserLoginVo> login(UserLoginDto userLoginDto) {
        String username = userLoginDto.getUsername();
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        if (user == null) {
            return new Result<UserLoginVo>().error(ResponseCode.USERNAME_OR_PASSWORD_INVALID);
        }
        // 获取用户键入的密码的hash值，与数据库中的密码进行比对
        if (!SHAUtil.getHash(userLoginDto.getPassword()).equals(user.getPassword())) {
            return new Result<UserLoginVo>().error(ResponseCode.USERNAME_OR_PASSWORD_INVALID);
        } else if (user.getStatus().equals(Status.DISABLE)) {
            return new Result<UserLoginVo>().error(ResponseCode.ACCOUNT_LOCKED);
        }
        return new Result<UserLoginVo>().success(UserLoginVo.builder()
                .username(username)
                // 生成token
                .token(JwtUtil.generateToken(user.getId().toString()))
                .build(), "登录成功~`");
    }

    /**
     * 注册
     */
    @Override
    public Result<UserRegisterVo> register(UserRegisterDto userRegisterDto) {
        if (!userRegisterDto.getPassword().equals(userRegisterDto.getConfirmPassword())) {
            throw new CustomException(ResponseCode.CONFIRM_PASSWORD_ERROR);
        }
        String username = userRegisterDto.getUsername();
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        if (user != null) {
            throw new CustomException(ResponseCode.USERNAME_EXIST);
        }
        User newUser = User.builder()
                .username(username)
                // 密码加密
                .password(SHAUtil.getHash(userRegisterDto.getPassword()))
                .createdTime(LocalDateTime.now())
                .status(Status.ENABLE)
                .build();
        userMapper.insert(newUser);
        return new Result<UserRegisterVo>().success(UserRegisterVo.builder()
                .username(username)
                // 生成token
                .token(JwtUtil.generateToken(newUser.getId().toString()))
                .build(), "注册成功~");
    }

    /**
     * 查询当前用户基本信息
     */
    @Override
    public Result<GetUserInfoVo> queryUserInfo() {
        Long userId = ThreadLocalUtil.getUserId();
        if (userId == null) {
            return new Result<GetUserInfoVo>().error(ResponseCode.NOT_LOGIN);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new CustomException(ResponseCode.USERNAME_OR_PASSWORD_INVALID);
        }
        GetUserInfoVo getUserInfoVo = new GetUserInfoVo();
        BeanUtils.copyProperties(user, getUserInfoVo, "avatar");
        getUserInfoVo.setAvatar(new OSSClientBuilder().build(aliOssProperties.getEndpoint(), aliOssProperties.getAccessKey(), aliOssProperties.getAccessSecretKey()).generatePresignedUrl(aliOssProperties.getBucketName(), user.getAvatar(), new Date(System.currentTimeMillis() + 3600 * 1000)).toString());
        return new Result<GetUserInfoVo>().success(getUserInfoVo, "查询成功~");
    }

    /**
     * 更新头像
     */
    @Override
    public Result<String> updateAvatar(@NonNull MultipartFile multipartFile) {
        if (multipartFile.getSize() == 0) {
            throw new CustomException(ResponseCode.FILE_EMPTY);
        }
        Long userId = ThreadLocalUtil.getUserId();
        if (userId == null) {
            throw new CustomException(ResponseCode.NOT_LOGIN);
        }
        byte[] bytes;
        try {
            bytes = multipartFile.getBytes();
        } catch (IOException e) {
            throw new CustomException(ResponseCode.FILE_READ_FAILED);
        }
        String originalFilename = multipartFile.getOriginalFilename();
        if (!StringUtils.hasLength(originalFilename)) {
            throw new CustomException(ResponseCode.FILE_NAME_IS_NULL);
        }
        String objectName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String url = ossFileUploader.uploadAndGetPresignedUrl(bytes, objectName);
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new CustomException(ResponseCode.USERNAME_OR_PASSWORD_INVALID);
        }
        String primaryAvatar = user.getAvatar();
        userMapper.updateById(User.builder().id(userId).avatar(objectName).build());
        // 待更新头像之后，删除原来的头像
        try {
            oss.deleteObject(aliOssProperties.getBucketName(), primaryAvatar);
        } catch (OSSException | ClientException e) {
            log.warn("从 Aliyun OSS 删除原头像失败！");
        }
        return new Result<String>().success(url, "更新头像成功~");
    }

    /**
     * 更新用户名
     */
    @Override
    public Result<?> updateUsername(@NonNull String username) {
        Long userId = ThreadLocalUtil.getUserId();
        if (userId == null) {
            throw new CustomException(ResponseCode.NOT_LOGIN);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new CustomException(ResponseCode.USERNAME_OR_PASSWORD_INVALID);
        }
        if (user.getUsername().equals(username)) {
            return new Result<>().success("用户名一样~");
        } else {
            try {
                User selectOne = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
                if (selectOne != null && !Objects.equals(selectOne.getId(), userId)) {
                    return new Result<>().error(ResponseCode.USERNAME_EXIST);
                }
            } catch (TooManyResultsException e) {
                log.error("用户名已存在，且存在多个！", e);
                throw new CustomException(ResponseCode.USERNAME_EXIST);
            }
        }
        userMapper.updateById(User.builder()
                .id(userId)
                .username(username)
                .build());
        return new Result<>().success("更新用户信息成功~");
    }

    /**
     * 更新自我介绍
     */
    @Override
    public Result<?> updateIntroduction(@NonNull String introduction) {
        Long userId = ThreadLocalUtil.getUserId();
        if (userId == null) {
            throw new CustomException(ResponseCode.NOT_LOGIN);
        }
        userMapper.updateById(User.builder()
                .id(userId)
                .introduction(introduction)
                .build());
        return new Result<>().success("更新自我介绍成功");
    }
}
